#include "CortexM3_V10.h"
#include <string.h>

/****************************UART*******************************/

/**
 *
 * @brief  Initialises the UART specifying the UART Baud rate divider value and whether the send and recieve functionality is enabled. It also specifies which of the various interrupts are enabled.
 *
 * @param *uart UART Pointer
 * @param divider The value to which the UART baud rate divider is to be set
 * @param tx_en Defines whether the UART transmit is to be enabled
 * @param rx_en Defines whether the UART receive is to be enabled
 * @param tx_irq_en Defines whether the UART transmit buffer full interrupt is to be enabled
 * @param rx_irq_en Defines whether the UART receive buffer full interrupt is to be enabled
 * @param tx_ovrirq_en Defines whether the UART transmit buffer overrun interrupt is to be enabled
 * @param rx_ovrirq_en Defines whether the UART receive buffer overrun interrupt is to be enabled
 * @return 1 if initialisation failed, 0 if successful.
 */
uint32_t  uart_init( UART_TypeDef *uart, uint32_t divider, uint32_t tx_en,
                        uint32_t rx_en, uint32_t tx_irq_en, uint32_t rx_irq_en, uint32_t tx_ovrirq_en, uint32_t rx_ovrirq_en)
{
    uint32_t new_ctrl=0;

    if (tx_en!=0)        new_ctrl |=  UART_CTRL_TXEN_Msk;
    if (rx_en!=0)        new_ctrl |=  UART_CTRL_RXEN_Msk;
    if (tx_irq_en!=0)    new_ctrl |=  UART_CTRL_TXIRQEN_Msk;
    if (rx_irq_en!=0)    new_ctrl |=  UART_CTRL_RXIRQEN_Msk;
    if (tx_ovrirq_en!=0) new_ctrl |=  UART_CTRL_TXORIRQEN_Msk;
    if (rx_ovrirq_en!=0) new_ctrl |=  UART_CTRL_RXORIRQEN_Msk;

        uart->CTRL = 0;         /* Disable UART when changing configuration */
        uart->BAUDDIV = divider;
        uart->CTRL = new_ctrl;  /* Update CTRL register to new value */

    if(( uart->STATE & ( UART_STATE_RXOR_Msk |  UART_STATE_TXOR_Msk))) return 1;
    else return 0;
}

/**
 *
 * @param *uart UART Pointer
 * @return RxBufferFull
 *
 * @brief  Returns whether the RX buffer is full.
 */
uint32_t  uart_GetRxBufferFull( UART_TypeDef * uart)
{
    return (( uart->STATE &  UART_STATE_RXBF_Msk)>>  UART_STATE_RXBF_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @return TxBufferFull
 *
 * @brief  Returns whether the TX buffer is full.
 */
uint32_t  uart_GetTxBufferFull( UART_TypeDef * uart)
{
    return (( uart->STATE &  UART_STATE_TXBF_Msk)>>  UART_STATE_TXBF_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @param txchar Character to be sent
 * @return none
 *
 * @brief  Sends a character to the TX buffer for transmission.
 */
void  uart_SendChar( UART_TypeDef * uart, char txchar)
{
    while( 1 ){
        if(!(uart->STATE &  UART_STATE_TXBF_Msk)) break;
    };
    uart->DATA = (uint32_t)txchar;
}

/**
 *
 * @param *uart UART Pointer
 * @return rxchar
 *
 * @brief  returns the character from the RX buffer which has been received.
 */
char  uart_ReceiveChar( UART_TypeDef * uart)
{
    while(!( uart->STATE &  UART_STATE_RXBF_Msk));
    return (char)( uart->DATA);
}

/**
 *
 * @param *uart UART Pointer
 * @return 0 - No overrun
 * @return 1 - TX overrun
 * @return 2 - RX overrun
 * @return 3 - TX & RX overrun
 *
 * @brief  returns the current overrun status of both the RX & TX buffers.
 */
uint32_t  uart_GetOverrunStatus( UART_TypeDef *uart)
{
    return (( uart->STATE & ( UART_STATE_RXOR_Msk |  UART_STATE_TXOR_Msk))>> UART_STATE_TXOR_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @return 0 - No overrun
 * @return 1 - TX overrun
 * @return 2 - RX overrun
 * @return 3 - TX & RX overrun
 *
 * @brief  Clears the overrun status of both the RX & TX buffers and then returns the current overrun status.
 */
uint32_t  uart_ClearOverrunStatus( UART_TypeDef *uart)
{
    uart->STATE = ( UART_STATE_RXOR_Msk |  UART_STATE_TXOR_Msk);
    return (( uart->STATE & ( UART_STATE_RXOR_Msk |  UART_STATE_TXOR_Msk))>> UART_STATE_TXOR_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @return BaudDiv
 *
 * @brief  Returns the current UART Baud rate divider. Note that the Baud rate divider is the difference between the clock frequency and the Baud frequency.
 */
uint32_t  uart_GetBaudDivider( UART_TypeDef *uart)
{
    return  uart->BAUDDIV;
}

/**
 *
 * @param *uart UART Pointer
 * @return TXStatus
 *
 * @brief  Returns the TX interrupt status.
 */
uint32_t  uart_GetTxIRQStatus( UART_TypeDef *uart)
{
    return (( uart->INTSTATUS &  UART_CTRL_TXIRQ_Msk)>> UART_CTRL_TXIRQ_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @return RXStatus
 *
 * @brief  Returns the RX interrupt status.
 */
uint32_t  uart_GetRxIRQStatus( UART_TypeDef *uart)
{
    return (( uart->INTSTATUS &  UART_CTRL_RXIRQ_Msk)>> UART_CTRL_RXIRQ_Pos);
}

/**
 *
 * @param *uart UART Pointer
 * @return none
 *
 * @brief  Clears the TX buffer full interrupt status.
 */
void  uart_ClearTxIRQ( UART_TypeDef *uart)
{
    uart->INTCLEAR =  UART_CTRL_TXIRQ_Msk;
}

/**
 *
 * @param *uart UART Pointer
 * @return none
 *
 * @brief  Clears the RX interrupt status.
 */
void  uart_ClearRxIRQ( UART_TypeDef *uart)
{
    uart->INTCLEAR =  UART_CTRL_RXIRQ_Msk;
}

/****************************GPIO*******************************/

/**
 *
 * @param *GPIO GPIO Pointer
 * @param outenable Bit pattern to be used to set output enable register
 * @return none
 *
 * @brief  Sets pins on a port as an output. Set the bit corresponding to the pin number to 1 for output i.e. Set bit 1 of outenable to 1 to set pin 1 as an output. This function is thread safe.
 */
void GPIO_SetOutEnable(GPIO_TypeDef *GPIO, uint32_t outenableset)
{
    GPIO->OUTENABLESET = outenableset;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param outenable Bit pattern to be used to set output enable register
 * @return none
 *
 * @brief  Sets pins on a port as an input. Set the bit corresponding to the pin number to 1 for input i.e. Set bit 1 of outenable to 1 to set pin 1 as an input. This function is thread safe.
 */
void GPIO_ClrOutEnable(GPIO_TypeDef *GPIO, uint32_t outenableclr)
{
    GPIO->OUTENABLECLR = outenableclr;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @return outputstatus
 *
 * @brief  returns a uint32_t which defines the whether pins on a port are set as inputs or outputs i.e. if bit 1 of the returned uint32_t is set to 1 then this means that pin 1 is an output.
 */
uint32_t GPIO_GetOutEnable(GPIO_TypeDef *GPIO)
{
    return GPIO->OUTENABLESET;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param AltFunc uint32_t to specify whether the alternate function for the pins on the port is enabled
 * @return none
 *
 * @brief  enables the alternative function for pins. Set the bit corresponding to the pin number to 1 for alternate function i.e. Set bit 1 of ALtFunc to 1 to set pin 1 to its alternative function. This function is thread safe.
 */
void GPIO_SetAltFunc(GPIO_TypeDef *GPIO, uint32_t AltFuncset)
{
    GPIO->ALTFUNCSET = AltFuncset;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param AltFunc uint32_t to specify whether the alternate function for the pins on the port is enabled
 * @return none
 *
 * @brief  disables the alternative function for pins. Set the bit corresponding to the pin number to 1 to disable alternate function i.e. Set bit 1 of ALtFunc to 1 to set pin 1 to the orignal output function. This function is thread safe.
 */
void GPIO_ClrAltFunc(GPIO_TypeDef *GPIO, uint32_t AltFuncclr)
{
    GPIO->ALTFUNCCLR = AltFuncclr;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @return AltFuncStatus
 *
 * @brief  returns a uint32_t which defines the whether pins on a port are set to their alternative or their original output functionality i.e. if bit 1 of the returned uint32_t is set to 1 then this means that pin 1 is set to its alternative function.
 */
uint32_t GPIO_GetAltFunc(GPIO_TypeDef *GPIO)
{
    return GPIO->ALTFUNCSET;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to clear the Interrupt
 * @return NewIntStatus
 *
 * @brief  Clears the interrupt flag for the specified pin and then returns the new interrupt status of the pin. This function is thread safe.
 */
uint32_t GPIO_IntClear(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTCLEAR = (1 << Num);

    return GPIO->INTSTATUS;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to enable the Interrupt
 * @return NewIntEnStatus
 *
 * @brief  Enables interrupts for the specified pin and then returns the new interrupt enable status of the pin. This function is thread safe.
 */
uint32_t GPIO_SetIntEnable(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTENSET = (1 << Num);

    return GPIO->INTENSET;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to disable the Interrupt
 * @return NewIntEnStatus
 *
 * @brief  Disables interrupts for the specified pin and then returns the new interrupt enable status of the pin. This function is thread safe.
 */
uint32_t GPIO_ClrIntEnable(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTENCLR = (1 << Num);

    return GPIO->INTENCLR;
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to set the Interrupt type
 * @return none
 *
 * @brief  Changes the interrupt type for the specified pin to a high level interrupt. This function is thread safe.
 */
void GPIO_SetIntHighLevel(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTTYPECLR = (1 << Num); /* Clear INT TYPE bit */
    GPIO->INTPOLSET = (1 << Num);  /* Set INT POLarity bit */
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to set the Interrupt type
 * @return none
 *
 * @brief  Changes the interrupt type for the specified pin to a rising edge interrupt. This function is thread safe.
 */
void GPIO_SetIntRisingEdge(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTTYPESET = (1 << Num); /* Set INT TYPE bit */
    GPIO->INTPOLSET = (1 << Num);  /* Set INT POLarity bit */
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to set the Interrupt type
 * @return none
 *
 * @brief  Changes the interrupt type for the specified pin to a low level interrupt. This function is thread safe.
 */
void GPIO_SetIntLowLevel(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTTYPECLR = (1 << Num);  /* Clear INT TYPE bit */
    GPIO->INTPOLCLR = (1 << Num);   /* Clear INT POLarity bit */
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param Num The pin number for which to set the Interrupt type
 * @return none
 *
 * @brief  Changes the interrupt type for the specified pin to a falling edge interrupt. This function is thread safe.
 */
void GPIO_SetIntFallingEdge(GPIO_TypeDef *GPIO, uint32_t Num)
{
    GPIO->INTTYPESET = (1 << Num);  /* Set INT TYPE bit */
    GPIO->INTPOLCLR = (1 << Num);   /* Clear INT POLarity bit */
}

/**
 *
 * @param *GPIO GPIO Pointer
 * @param mask The output port mask
 * @param value The value to output to the specified port
 * @return none
 *
 * @brief Outputs the specified value on the desired port using the user defined mask to perform Masked access.
 */
void GPIO_MaskedWrite(GPIO_TypeDef *GPIO, uint32_t value, uint32_t mask)
{
    GPIO->LB_MASKED[0x00FF & mask] = value;
    GPIO->UB_MASKED[((0xFF00 & mask) >> 8)] = value;
}


/**************************************SYSTICK*******************************************/

const uint32_t us_value = 50000000/1000000;
const uint32_t ms_value = 50000000/1000;

/**
 * @brief Initialize usvalue & msvalue with system core clock.
 */
void Delay_Init(uint32_t SystemCoreClock) {
    SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
}

/**
 * @brief delay n us.
 */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus*us_value;
    SysTick->VAL = 0;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do
    {
        temp = SysTick->CTRL;
    } while ((temp&SysTick_CTRL_ENABLE_Msk) && !(temp&SysTick_CTRL_COUNTFLAG_Msk));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0;
    return;
}

/**
 * @brief delay n ms, n<=300.
 */
void delay_ms(uint32_t nms)
{
    uint32_t temp;
    SysTick->LOAD = nms*ms_value;
    SysTick->VAL = 0;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do
    {
        temp = SysTick->CTRL;
    } while ((temp&SysTick_CTRL_ENABLE_Msk) && !(temp&SysTick_CTRL_COUNTFLAG_Msk));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0;
    return;
}
